package dynamic.jdk.theory;

import dynamic.jdk.Seller;

import javax.tools.*;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class MyProxy {
    public static final String RN = "\r\n";
    public static Object newProxyInstance(MyClassLoader myClassLoader, Class<?> interfaces, InvocationHandler invocationHandler) {
        //1.生成代理类源码.java
        produceSrc(interfaces);
        //2.编译.java文件为.class
        compilerJava();
        //3.加载.class文件
        Class<?> clazz = null;
        try {
            clazz = myClassLoader.findClass("dynamic.jdk.theory.$Proxy");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //4.反射生成代理类对象
        if (clazz != null){
            try {
                Constructor<?> constructor = clazz.getConstructor(InvocationHandler.class);
                return constructor.newInstance(invocationHandler);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 编译.java文件为.class
     */
    private static void compilerJava() {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMangeer = compiler.getStandardFileManager(null,null,null);
        File f = new File(MyProxy.class.getResource("").getPath()+"/$Proxy.java");
        Iterable<? extends JavaFileObject> com = fileMangeer.getJavaFileObjects(f);
        JavaCompiler.CompilationTask task = compiler.getTask(null,fileMangeer,null,null,null,com);
        task.call();
        f.delete();
    }

    /**
     * 生成代理类源码.java
     * @param interfaces
     */
    private static void produceSrc(Class<?> interfaces) {
        StringBuilder builder = new StringBuilder();
        builder.append("package dynamic.jdk.theory;"+RN);
        builder.append("import java.lang.reflect.Method;"+RN);
        builder.append("public class $Proxy implements "+interfaces.getName()+"{"+RN);
        builder.append("private "+InvocationHandler.class.getName()+" h;"+RN);
        builder.append("public $Proxy("+InvocationHandler.class.getName()+" h) {"+RN);
        builder.append("this.h = h;");
        builder.append("}"+RN);
        Method[] methods= interfaces.getMethods();
        for (Method method : methods){
            builder.append("public "+method.getReturnType()+" "+method.getName()+"() {"+RN);
            builder.append("try {"+RN);
            builder.append("Method method = "+interfaces.getName()+".class.getMethod(\""+method.getName()+"\",new Class[]{});"+RN);
            builder.append("this.h.invoke(this, method, null);"+RN);
            builder.append("}catch(Throwable e) {"+RN);
            builder.append("}"+RN);
            builder.append("}"+RN);
        }
        builder.append("}");
        String src = builder.toString();
        String path = MyProxy.class.getResource("").getPath();
        try {
            FileOutputStream fos = new FileOutputStream(new File(path) + "/$Proxy.java");
            fos.write(src.getBytes());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
//        String path = MyProxy.class.getResource("").getPath();
//        System.out.println(path);
        newProxyInstance(null,Seller.class.getInterfaces()[0],null);
    }
}
